package com.sl.repository.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;

import com.sl.entity.dto.OrganDTO;
import com.sl.entity.dto.TransportLineNodeDTO;
import com.sl.entity.node.AgencyEntity;
import com.sl.enums.OrganTypeEnum;
import com.sl.repository.TransportLineRepository;
import org.neo4j.driver.internal.value.PathValue;
import org.neo4j.driver.types.Path;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;

@Component
public class TransportLineRepositoryImpl implements TransportLineRepository {

    @Resource
    private Neo4jClient neo4jClient;

    @Override
    public TransportLineNodeDTO findShortestPath(AgencyEntity start, AgencyEntity end) {
        // 1. 定义Cypher查询语句：查找两个AGENCY节点间的最短路径（最多10跳）
        //    shortestPath：Neo4j内置函数，用于计算最短路径
        //    (n:AGENCY)：起始节点，标签为AGENCY（代表网点）
        //    [*..10]：关系跳转次数范围（最少0跳，最多10跳，避免无限循环）
        //    (m:AGENCY)：终点节点，标签为AGENCY
        //    WHERE子句：通过bid属性匹配起始和终点的唯一标识
        String cypherStr = "MATCH path = shortestPath((n:AGENCY) -[*..10]->(m:AGENCY)) " +
                "WHERE n.bid = $startId AND m.bid = $endId RETURN path";
        //获取网点数据在Neo4j中的类型
        //    Node注解：Spring Data Neo4j中用于标记实体对应的图节点标签
        //    value()[0]：获取注解中定义的第一个标签名（如"AGENCY"）
        String type = AgencyEntity.class.getAnnotation(Node.class).value()[0];


        //执行查询
        Optional<TransportLineNodeDTO> optional = this.neo4jClient.query(cypherStr)
                .bind(start.getBid()).to("startId") //设置参数
                .bind(end.getBid()).to("endId")//设置参数
                .fetchAs(TransportLineNodeDTO.class) //设置响应数据类型
                .mappedBy((typeSystem, record) -> { //对结果进行封装处理
                    // 从查询结果中提取路径对象（Neo4j的Path类型）
                    PathValue pathValue = (PathValue) record.get(0);
                    Path path = pathValue.asPath();
                    TransportLineNodeDTO dto = new TransportLineNodeDTO();
                    // 读取节点数据
                    path.nodes().forEach(node -> {
                        Map<String, Object> map = node.asMap();
                        OrganDTO organDTO = BeanUtil.toBeanIgnoreError(map, OrganDTO.class);

                        //取第一个标签作为类型
                        organDTO.setType(OrganTypeEnum.valueOf(CollUtil.getFirst(node.labels())).getCode());
                        //查询出来的数据，x：经度，y：纬度
                        organDTO.setLatitude(BeanUtil.getProperty(map.get("location"), "y"));
                        organDTO.setLongitude(BeanUtil.getProperty(map.get("location"), "x"));

                        dto.getNodeList().add(organDTO);
                    });

                    //提取关系中的 cost 数据，进行求和计算，算出该路线的总成本
                    path.relationships().forEach(relationship -> {
                        Map<String, Object> objectMap = relationship.asMap();
                        double cost = Convert.toDouble(objectMap.get("cost"), 0d);
                        dto.setCost(NumberUtil.add(cost, dto.getCost().doubleValue()));
                    });

                    //取2位小数
                    dto.setCost(NumberUtil.round(dto.getCost(), 2).doubleValue());
                    return dto;
                }).one();

        // 返回结果（无结果时返回null）
        return optional.orElse(null);
    }
}
